﻿using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.DataStructures;
using Terraria.ID;
using Terraria.ModLoader;
using Terraria.ModLoader.IO;

namespace HardcoreGame.Common.Systems
{
    internal enum FestivalMessageType : byte
    {
        SyncFestivalData
    }
    public class FestivalTrashCanSystem : ModSystem
    {
        public static readonly (int Month, int Day)[] FestivalDates = {
            (2, 14),
            (5, 20),
        };
        public Dictionary<Point16, DateTime> processedTrashCans = [];
        public override void OnWorldLoad()
        {
            processedTrashCans.Clear();
        }
        public override void LoadWorldData(TagCompound tag)
        {
            if (tag.ContainsKey("processedTrashCans"))
            {
                var list = tag.GetList<TagCompound>("processedTrashCans");
                processedTrashCans.Clear();

                foreach (var entry in list)
                {
                    Point16 position = entry.Get<Point16>("position");
                    DateTime date = DateTime.Parse(entry.GetString("date"));
                    processedTrashCans[position] = date;
                }
            }
        }
        public override void SaveWorldData(TagCompound tag)
        {
            if (processedTrashCans.Count > 0)
            {
                var list = new List<TagCompound>();

                foreach (var entry in processedTrashCans)
                {
                    var tagEntry = new TagCompound
                    {
                        ["position"] = entry.Key,
                        ["date"] = entry.Value.ToString("yyyy-MM-dd")
                    };
                    list.Add(tagEntry);
                }

                tag["processedTrashCans"] = list;
            }
        }
        public override void PostUpdateTime()
        {
            if (Main.netMode == NetmodeID.Server || Main.netMode == NetmodeID.SinglePlayer)
            {
                if (Main.dayTime && Main.time == 0)
                {
                    CheckAndProcessFestivals();
                    if (Main.netMode == NetmodeID.Server)
                    {
                        SyncFestivalData();
                    }
                }
            }
        }
        private void SyncFestivalData()
        {
            ModPacket packet = Mod.GetPacket();
            packet.Write((byte)FestivalMessageType.SyncFestivalData);
            packet.Write(processedTrashCans.Count);

            foreach (var entry in processedTrashCans)
            {
                packet.Write(entry.Key.X);
                packet.Write(entry.Key.Y);
                packet.Write(entry.Value.ToString("yyyy-MM-dd"));
            }

            packet.Send();
        }
        private void CheckAndProcessFestivals()
        {
            DateTime today = DateTime.Now;
            if (IsFestivalDay(today))
            {
                ProcessFestivalTrashCans(today);
            }
        }
        private void ProcessFestivalTrashCans(DateTime today)
        {
            for (int i = 0; i < Main.maxTilesX; i++)
            {
                for (int j = 0; j < Main.maxTilesY; j++)
                {
                    if (Main.tile[i, j].TileType == 21)
                    {
                        int left = i;
                        int top = j;
                        if (Main.tile[i, j].TileFrameX % 36 != 0 || Main.tile[i, j].TileFrameY % 36 != 0)
                            continue;

                        Point16 position = new(left, top);
                        if (!processedTrashCans.TryGetValue(position, out DateTime lastProcessed) || lastProcessed < today.Date)
                        {
                            int chestIndex = Chest.FindChest(left, top);
                            if (chestIndex != -1)
                            {
                                Chest chest = Main.chest[chestIndex];
                                if (Main.rand.NextFloat() < 0.3f)
                                {
                                    AddRandomItemToChest(chest);
                                    if (Main.netMode != NetmodeID.Server)
                                    {
                                        for (int k = 0; k < 15; k++)
                                        {
                                            Dust.NewDust(new Vector2(left * 16, top * 16), 32, 32, DustID.GoldCoin, Scale: 1.2f);
                                        }
                                    }
                                }
                                processedTrashCans[position] = today.Date;
                            }
                        }
                    }
                }
            }
        }
        private static void AddRandomItemToChest(Chest chest)
        {
            for (int i = 0; i < Chest.maxItems; i++)
            {
                if (chest.item[i].type == ItemID.None)
                {
                    int itemType = GetRandomFestivalItem();
                    int stack = 1;
                    if (itemType == ItemID.CopperCoin || itemType == ItemID.SilverCoin || itemType == ItemID.GoldCoin)
                    {
                        stack = Main.rand.Next(5, 20);
                    }
                    chest.item[i].SetDefaults(itemType);
                    chest.item[i].stack = stack;
                    break;
                }
            }
        }
        private static int GetRandomFestivalItem()
        {
            DateTime today = DateTime.Now;
            if (today.Month == 2 && today.Day == 14)
            {
                int[] valentinesItems = {
                    1109,
                };
                return valentinesItems[Main.rand.Next(valentinesItems.Length)];
            }

            else if (today.Month == 5 && today.Day == 20)
            {
                int[] loveItems = {
                    1109,
                };
                return loveItems[Main.rand.Next(loveItems.Length)];
            }
            return ItemID.CopperCoin;
        }
        private static bool IsFestivalDay(DateTime date)
        {
            foreach (var (month, day) in FestivalDates)
            {
                if (date.Month == month && date.Day == day)
                {
                    return true;
                }
            }
            return false;
        }
    }
}